--- /dev/null
--- /dev/null
++cmus (2.12.0-2) unstable; urgency=medium
++
++ * Team upload
++ * Fix build with ffmpeg 8.0 (Closes: #1115013)
++
++ -- Sebastian Ramacher <sramacher@debian.org> Fri, 12 Sep 2025 23:00:35 +0200
++
++cmus (2.12.0-1) unstable; urgency=medium
++
++ * Team upload.
++
++ [ Philippe SWARTVAGHER ]
++ * New upstream version 2.12.0 (Closes: #1087737)
++ + Refresh patches
++ * Bump standards-version to 4.7.2: no change needed
++ * d/rules: remove as-needed linker flag
++ * Add lintian override for library-not-linked-against-libc
++ of usr/lib/cmus/op/sndio.so
++ * Add patch to use hardening flags to build Doc/ttman
++
++ [ Helmut Grohne ]
++ * Fix FTCBFS: Fix build/host confusion. (Closes: #954749)
++
++ [ Dylan Aïssi ]
++ * Convert debian/copyright to DEP5
++
++ -- Philippe SWARTVAGHER <phil.swart@gmx.fr> Sat, 15 Mar 2025 22:56:48 +0100
++
++cmus (2.11.0-1) unstable; urgency=medium
++
++ * Team upload
++ * New upstream version 2.11.0
++ - Fix build with ffmpeg 7.0 (Closes: #1072405)
++ * debian/patches: Refresh patches
++ * debian/control:
++ - Bump Standards-Version
++ - Use pkgconf
++ - Use openmpt's modplug
++ - Use libncurses-dev
++
++ -- Sebastian Ramacher <sramacher@debian.org> Sun, 09 Jun 2024 23:45:17 +0200
++
++cmus (2.10.0-4) unstable; urgency=medium
++
++ * Team upload
++ * debian/patches: Apply upstream patch to fix compatibility with ffmpeg 6.0
++ (Closes: #1041375)
++
++ -- Sebastian Ramacher <sramacher@debian.org> Thu, 27 Jul 2023 21:00:31 +0200
++
++cmus (2.10.0-3) unstable; urgency=medium
++
++ * Team upload
++ * debian/control: Bump Standards-Version
++ * debian/: Remove roar support (Closes: #1030661)
++
++ -- Sebastian Ramacher <sramacher@debian.org> Tue, 07 Feb 2023 09:20:50 +0100
++
++cmus (2.10.0-2) unstable; urgency=medium
++
++ * Team upload
++ * Add a patch from upstream to fix a freeze when exiting cmus
++
++ -- Philippe SWARTVAGHER <phil.swart@gmx.fr> Sun, 31 Jul 2022 22:01:04 +0200
++
++cmus (2.10.0-1) unstable; urgency=medium
++
++ * Team upload
++
++ [ Jenkins ]
++ * Remove constraints unnecessary since buster
++
++ [ Philippe SWARTVAGHER ]
++ * New upstream version 2.10.0
++ * Bump d/watch version
++ * Bump standards-version to 4.6.1
++ * Add a patch to fix a typo spotted by Lintian
++
++ -- Philippe SWARTVAGHER <phil.swart@gmx.fr> Wed, 13 Jul 2022 20:40:37 +0200
++
++cmus (2.9.1-1) unstable; urgency=medium
++
++ * Team upload
++ * New upstream release
++
++ -- Sebastian Ramacher <sramacher@debian.org> Fri, 22 Jan 2021 22:28:04 +0100
++
++cmus (2.9.0-1) unstable; urgency=medium
++
++ * Team upload
++
++ [ Debian Janitor ]
++ * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
++ Repository-Browse.
++
++ [ Patrick Gaskin ]
++ * Fix missing dependency for MPRIS support
++
++ [ Sebastian Ramacher ]
++ * New upstream release
++ * debian/control:
++ - Bump Standards-Version
++ - Bump debhelper compat to 13
++ - Set RRR: no
++ * debian/patches/12-typos.patch: Removed, fixed upstream
++
++ -- Sebastian Ramacher <sramacher@debian.org> Tue, 19 Jan 2021 20:15:26 +0100
++
++cmus (2.8.0-2) unstable; urgency=medium
++
++ * Set build flags via /usr/share/dpkg/buildflags.mk
++ * Link with -latomic to fix FTBFS on various architectures (Closes: #935678)
++
++ -- Ryan Kavanagh <rak@debian.org> Sat, 07 Sep 2019 10:37:13 -0400
++
++cmus (2.8.0-1) unstable; urgency=medium
++
++ [ Ondřej Nový ]
++ * Use debhelper-compat instead of debian/compat
++
++ [ Helmut Grohne ]
++ * Fix FTCBFS: Supply a cross environment to ./configure. (Closes: #911163)
++
++ [ Ryan Kavanagh ]
++ * New upstream version 2.8.0 (Closes: #932107, #783498)
++ + Update copyright file with new holders
++ + Drop 12-ffmpeg-4.0.patch (applied upstream)
++ + Refresh patches
++ * Drop outdated get-orig-source target from rules
++ * Fix typos in source, 12-typos.patch
++ * Enable hardening
++ * Bump debphelper compat to 12
++ * Bump standards-version to 4.4.0
++ * cmus-plugin-ffmpeg (<< 2.8.0) breaks cmus (>= 2.8.0)
++
++ -- Ryan Kavanagh <rak@debian.org> Sat, 17 Aug 2019 14:51:27 -0400
++
++cmus (2.7.1+git20160225-2) unstable; urgency=medium
++
++ * Team upload.
++
++ [ James Cowgill ]
++ * Add upstream patch to fix FTBFS with FFmpeg 4.0. (Closes: #888384)
++ * d/changelog: Remove trailing blank line.
++
++ [ Alessio Treglia ]
++ * Remove myself from the Uploaders field.
++
++ [ Ondřej Nový ]
++ * d/control: Set Vcs-* to salsa.debian.org.
++ * d/changelog: Remove trailing whitespaces.
++
++ [ Felipe Sateler ]
++ * Change maintainer address to debian-multimedia@lists.debian.org.
++
++ -- James Cowgill <jcowgill@debian.org> Thu, 17 May 2018 11:34:35 +0100
++
++cmus (2.7.1+git20160225-1) unstable; urgency=medium
++
++ * Team upload.
++ * New upstream snapshot.
++ - Fix build against ffmpeg 3.0. (Closes: #810557)
++ * debian/patches/{01_config.mk.diff,02_link_avcodec.patch}: Removed, applied
++ upstream.
++ * debian/control:
++ - Bump Standards Version.
++ - Update Vcs-Git.
++
++ -- Sebastian Ramacher <sramacher@debian.org> Sun, 06 Mar 2016 21:37:57 +0100
++
++cmus (2.7.1-1) unstable; urgency=medium
++
++ * Team upload.
++
++ [ Alessio Treglia ]
++ * Demote extra plugins to Suggests (Closes: #789256)
++ * Refresh patchset for 2.6.0.
++
++ [ Sebastian Ramacher ]
++ * New upstream release. (Closes: #779335, #792134)
++ - Use libswresample instead of libavresample. (Closes: #805169, #805109)
++ * Update path for README
++ * debian/control:
++ - Add libdiscid-dev, libopusfile-dev, libsamplerate0-dev and libjack-dev
++ to Build-Depends.
++ - Change libavresample-dev to libswresample-dev in Build-Depends and add
++ libavcodec-dev.
++ - Add bash-completion to Build-Depends.
++ - Bump Standards-Version to 3.9.6.
++ - Update Vcs-Browser.
++ - Make cmus-plugin-ffmpeg depend on the same version of cmus (Closes:
++ #695072)
++ * debian/cmus.install: Install zsh completion.
++ * debian/cmus.bash-completion: Install bash completion.
++ * debian/rules:
++ - Build with --parallel and --with bash-completion.
++ - Handle jack shlibs similar to pulse.
++ * debian/patches:
++ - libav10.patch: Removed, no longer needed.
++ - 02_link_avcodec.patch: Functions from libavcodec are used so make sure
++ the ffmpeg plugin is linked against libavcodec.
++
++ -- Sebastian Ramacher <sramacher@debian.org> Sun, 15 Nov 2015 19:24:52 +0100
++
++cmus (2.5.0-7) unstable; urgency=medium
++
++ * Re-introduce Roaraudio support (Closes: #680745):
++ - debian/control: Add build-dependency on libroad-dev.
++ - debian/rules: Tune dpkg-shlibdeps call to move pulse and roar's
++ dependencies to Recommends. Made the whole mechanism slightly more
++ elegant.
++ * Enable CUE support.
++ * The project has moved to github, update the Homepage field accordingly.
++ * Update debian/watch, project has moved from sourceforge to github.
++
++ -- Alessio Treglia <alessio@debian.org> Thu, 14 Aug 2014 13:45:10 +0100
++
++cmus (2.5.0-6) unstable; urgency=medium
++
++ * Team upload.
++ * Upload to unstable.
++
++ -- Sebastian Ramacher <sramacher@debian.org> Sun, 11 May 2014 23:45:16 +0200
++
++cmus (2.5.0-5) experimental; urgency=low
++
++ * Team upload.
++ * Compile against libav10 (Closes: #739301)
++ * Bump standards version
++
++ -- Reinhard Tartler <siretart@tauware.de> Mon, 24 Mar 2014 19:35:28 -0400
++
++cmus (2.5.0-4) unstable; urgency=low
++
++ [ Ryan Kavanagh ]
++ * Patches were applied upstream
++
++ [ Alessio Treglia ]
++ * Add patch to prevent FTBFS. (Closes: #724181)
++
++ -- Alessio Treglia <alessio@debian.org> Sun, 06 Oct 2013 20:46:25 +0100
++
++cmus (2.5.0-3) unstable; urgency=low
++
++ * Don't FTBFS due to missing config.mk (Closes: #720781), 01_config.mk.diff
++ * Fix typo in cmus binary, 02_fix_typo.diff
++ * Enable build hardening
++ + Bump debhelper version to 9.0.0 and compat to 9
++ + Introduce 03_cppflags.diff to use CPPFLAGS; needed for function
++ fortification
++ + Override hardening-no-fortify-functions false positives
++ * Bump standards version to 3.9.4
++ * Switch to canonical Vcs-* fields
++ * Enable verbose build logs
++
++ -- Ryan Kavanagh <rak@debian.org> Thu, 29 Aug 2013 13:48:30 -0400
++
++cmus (2.5.0-2) unstable; urgency=low
++
++ * Upload to unstable.
++
++ -- Alessio Treglia <alessio@debian.org> Sat, 11 May 2013 01:21:18 +0200
++
++cmus (2.5.0-1) experimental; urgency=low
++
++ * New upstream release:
++ - CUE sheets support.
++ - cdio input plugin.
++ - support for WavPack `.wvc` correction files.
++ - new «zenburn» color scheme and text attributes (bold/reverse/...)
++ support for UI elements.
++ - improved tab completion, new scroll_offset and icecast_default_charset
++ options, even better tag parsing and compilations handling, and
++ numerous small enhancements all over the place.
++ * Build-depend on libcddb2-dev,libcdio-cdda-dev.
++
++ -- Alessio Treglia <alessio@debian.org> Thu, 15 Nov 2012 00:28:20 +0000
++
++cmus (2.4.3-2) unstable; urgency=low
++
++ [ Ryan Kavanagh ]
++ * Update my email address to @debian.org
++ * Drop DM-Upload-Allowed: yes, no longer needed
++
++ [ Alessio Treglia ]
++ * Build cmus without roar support. (Closes: #675610)
++ * Bump Standards.
++
++ -- Alessio Treglia <alessio@debian.org> Sat, 02 Jun 2012 20:07:57 +0200
++
++cmus (2.4.3-1) unstable; urgency=low
++
++ * New upstream release.
++
++ -- Alessio Treglia <alessio@debian.org> Sat, 03 Dec 2011 12:55:46 +0100
++
++cmus (2.4.2-1) unstable; urgency=low
++
++ * New upstream release.
++ * Drop 0001-fix-compile-error-for-new-versions-of-ffmpeg.patch,
++ applied upstream.
++
++ -- Alessio Treglia <alessio@debian.org> Tue, 26 Jul 2011 10:13:25 +0200
++
++cmus (2.4.1-2) unstable; urgency=low
++
++ * Add Ubuntu-specific patch to fix FTBFS with newest version of ffmpeg.
++ * Replace negated list of architectures with linux-any (Closes: #634706).
++
++ -- Alessio Treglia <alessio@debian.org> Sat, 23 Jul 2011 10:48:29 +0200
++
++cmus (2.4.1-1) unstable; urgency=medium
++
++ * New upstream release (Closes: #628422):
++ - Doc: add help for :shell
++ - ffmpeg: move up "config/ffmpeg.h" include
++ - fix two memleaks
++ - fix cache refresh bug
++ - configure: fix FLAC include path
++ - configure: fix ffmpeg header detection
++ - fix TCP/IP networking protocol
++ - fix segfault when hitting win-activate on empty tree
++ - display error if seeking failed
++ - fix segfault when using tqueue/lqueue
++ - fix lqueue command
++ - fix infinite loop when adding certain mp3 files
++ - fix reading of id3v2 tags at the end of files
++ - more fault-tolerant integer tag-reading
++ * Bump urgency to medium as the previous release was seriously buggy.
++
++ -- Alessio Treglia <alessio@debian.org> Sun, 29 May 2011 19:18:48 +0200
++
++cmus (2.4.0-1) unstable; urgency=low
++
++ * New upstream release "Easter egg":
++ - Mutt-like short filters.
++ - Live filtering.
++ - Resume support.
++ - Smarter string handling.
++ - Long format options, including ones for bitrate/codec.
++ - HTTP proxy support for streams via http_proxy environment variable.
++ - Less CPU wakeups during playback.
++ - New RoarAudio output plugin.
++ - Support for big-endian systems, lots of different audio sample formats,
++ almost any C compiler and unix-like OS out there.
++ - Various bugfixes.
++ - Full release notes are available at:
++ http://sourceforge.net/mailarchive/message.php?msg_id=27403242
++ * debian/watch: Properly handle release-candidate,beta releases.
++ * Remove debian/patches directory, all patches have been applied upstream.
++ * Bump Standards.
++
++ -- Alessio Treglia <alessio@debian.org> Tue, 26 Apr 2011 23:30:07 +0200
++
++cmus (2.3.5-1) unstable; urgency=low
++
++ * New upstream release:
++ - Features gapless MP3 playback.
++ - Native PulseAudio support.
++ - Faster startup.
++ - Improve buildsystem.
++ * Refresh patches.
++ * Remo 21-missing_plugins.patch, applied upstream.
++
++ -- Alessio Treglia <alessio@debian.org> Sat, 23 Apr 2011 09:56:57 +0200
++
++cmus (2.3.4-3) unstable; urgency=low
++
++ * Handle missing dependencies more gracefully:
++ - cmus silently skips plugins with missing dependencies, and instead
++ outputs a debug message. Original patch by Johannes Weißl, already
++ accepted upstream.
++
++ -- Alessio Treglia <alessio@debian.org> Fri, 01 Apr 2011 08:59:01 +0200
++
++cmus (2.3.4-2) unstable; urgency=low
++
++ * Avoid to depend on several sound servers (Closes: #612887) and let
++ users choose to rely on the favorite one.
++ - debian/control:
++ + Add shlibs:Recommends field.
++ - debian/rules:
++ + Supply {dh_,dpkg-}shlibdeps with proper options to demote roar
++ and pulse audio dependencies to Recommends.
++
++ -- Alessio Treglia <alessio@debian.org> Tue, 15 Mar 2011 12:46:03 +0100
++
++cmus (2.3.4-1) unstable; urgency=low
++
++ [ Ryan Kavanagh ]
++ * New upstream release.
++ * Dropped 01_spelling_mistakes.diff, 02_cmus-tutorial_whatis.diff
++ and 03-terminal_corruption.patch (applied upstream).
++ * Refreshed 10-roaraudio_support.patch
++ * Bump my copyright
++
++ [ Alessio Treglia ]
++ * Add DM-Upload-Allowed: yes.
++
++ -- Ryan Kavanagh <ryanakca@kubuntu.org> Tue, 22 Feb 2011 09:03:23 -0500
++
++cmus (2.3.3-4) unstable; urgency=low
++
++ * Upload to unstable.
++
++ -- Alessio Treglia <alessio@debian.org> Wed, 09 Feb 2011 12:05:49 +0100
++
++cmus (2.3.3-3) experimental; urgency=low
++
++ * Add RoarOutput plugin (Closes: #609202), thanks to
++ Philipp Schafft <lion@lion.leolix.org> for the patch.
++ * Add patch taken from upstream's git to fix segfault when adding to
++ queue.
++ * Build-depends on libroar-dev (>= 0.4~beta2).
++
++ -- Alessio Treglia <alessio@debian.org> Mon, 17 Jan 2011 02:23:04 +0100
++
++cmus (2.3.3-2) unstable; urgency=low
++
++ * Prevent terminal corruption on track change.
++ * debian/copyright: Update sources download location.
++ * Split cmus to provide smart dependencies (Closes: #442423).
++ * Bump Standards.
++
++ -- Alessio Treglia <alessio@debian.org> Sun, 01 Aug 2010 12:26:08 +0200
++
++cmus (2.3.3-1) unstable; urgency=low
++
++ [ Ryan Kavanagh ]
++ * New upstream release (Closes: #572284)
++ * Imported Upstream version 2.3.3
++ * Changed to source format 3.0 source. Involved converting
++ dpatch->quilt and dropping dpatch B-D.
++ * Dropped 01_cmusffmpeg.diff, no longer needed. Upstream checks for
++ ffmpeg and falls back to libavcodec for us.
++ * Dropped 02_cmusstatusdisplay.diff, included upstream.
++ * Dropped Yavor's mpcdec patch. Upstream expanded on it, using his
++ code if building under MPC SV8, the old code otherwise.
++ * Updated debian/watch
++ * Updated copyright file with new copyright holders and download
++ location.
++ * This is a new upload, package will thus be rebuilt against libavformat.
++ (Closes: #568361).
++ * Dropped debian/patches directory
++ * Move to debhelper 7 rules
++ * Now standards-version 3.8.4
++ * Fix debhelper-but-no-misc-depends lintian warning
++ * Override dh_auto_configure because upstream uses a homebrewed configure
++ script that doesn't accept --a=b style options
++ * Bump debhelper version to (>= 7.0.50~) because we're using override_dh_*
++ * Fix spelling mistakes in binary and documentation
++ (01_spelling_mistake.diff)
++ * Fix whatis entry for cmus-tutorial manpage (02_cmus-tutorial_whatis.diff)
++ * Added Julien Louis' and my own packaging copyright blurb to
++ debian/copyright.
++
++ [ Alessio Treglia ]
++ * This isn't a non-maintainer upload, we adopt this (Closes: #587604).
++ * Add debian/gbp.conf file.
++ * debian/control:
++ - Add Vcs-* tags.
++ - Bump Standards.
++ - Lines should be shorter than 80 characters.
++ - Add myself to Uploaders field, I'll take care of sponsoring this in
++ future.
++ - Build-depend on pkg-config.
++ - Drop unnecessary Recommends field.
++ * Drop aRTs support as it is no longer maintained.
++ * Add PulseAudio support.
++ * debian/rules:
++ - Call configure script instead of relying on dh_auto_configure.
++ * Drop README.source, we don't rely on dpatch as patch system.
++ * Update debian/copyright.
++
++ -- Alessio Treglia <alessio@debian.org> Sun, 04 Jul 2010 20:06:58 +0200
++
++cmus (2.2.0-4.1) unstable; urgency=low
++
++ * NMU
++ * Patch from Yavor Doganov to port cmus to the new mpcdec API,
++ thereby allowing cmus to build from source again.
++ closes: #476382, #552820.
++
++ -- Clint Adams <schizo@debian.org> Sun, 31 Jan 2010 00:03:40 -0500
++
++cmus (2.2.0-4) unstable; urgency=low
++
++ * Updated debian/watch file Closes: #449897
++ - Thanks to Raphael Geissert <atomo64@gmail.com>
++ * Fix the ffmpeg/avcodec.h includes Closes: #517570
++ - Thanks to Cyril Brulebois <kibi@debian.org>
++ * Added Recommends libasound2, libartsc0, libao2 Closes: #439719
++ * Added Depends line in debian/control
++ * Added dpatch on Build-Depends on debian/control
++ * Deleted commented lines on debian/rules
++ * Update debian/rules for dpatch dependence
++ * Changed debian/control
++ - Standard-Version to 3.8.1 ( was 3.8.0 )
++ - Added debian/README.source
++ - Updated to debhelper to 7
++ - Updated debian/compat (was 5)
++ - Added Homepage field
++
++ -- Carlos Eduardo Sotelo Pinto (krlos) <krlos.aqp@gmail.com> Thu, 19 Mar 2009 13:38:16 -0500
++
++cmus (2.2.0-3) unstable; urgency=low
++
++ * Acknowledging NMU. Closes: #509277.
++
++ -- Carlos Eduardo Sotelo Pinto (krlos) <krlos.aqp@gmail.com> Mon, 29 Dec 2008 22:01:01 +0100
++
++cmus (2.2.0-2) unstable; urgency=low
++
++ * New maintainer. Closes: #484734
++ * Changed debian/control
++ - Standard-Version to 3.8.0 ( was 3.7.2 no changes needed )
++
++ -- Carlos Eduardo Sotelo Pinto (krlos) <krlos.aqp@gmail.com> Wed, 03 Sep 2008 17:46:50 -0500
++
++cmus (2.2.0-1.1) unstable; urgency=high
++
++ * Non-maintainer upload by the Security Team.
++ * Modify example script cmus-status-display to write the current
++ status to .cmus-status in the user's home instead of /tmp/cmus-status,
++ since the latter could lead to symlink attacks. CVE-2008-5375
++ (Closes: #509277)
++
++ -- Moritz Muehlenhoff <jmm@debian.org> Sun, 28 Dec 2008 14:57:06 +0100
++
++cmus (2.2.0-1) unstable; urgency=low
++
++ * New upstream release
++ * Add libwavpack-dev and libavformat.dev to Build-Depends
++ * Update debian/copyright
++
++ -- Julien Louis <ptitlouis@sysif.net> Fri, 27 Jul 2007 21:54:36 +0200
++
++cmus (2.1.0-2) unstable; urgency=low
++
++ * Rebuild against libflac8 (Closes: #426638).
++
++ -- Julien Louis <ptitlouis@sysif.net> Mon, 04 Jun 2007 22:56:58 +0200
++
++cmus (2.1.0-1) unstable; urgency=low
++
++ * New upstream release (closes: #399965).
++ * Updated debian/copyright
++ * Added libfaad-dev to Build-Depends
++
++ -- Julien Louis <ptitlouis@sysif.net> Thu, 21 Dec 2006 20:25:59 +0100
++
++cmus (2.0.4-1) unstable; urgency=low
++
++ * New upstream release.
++ * Added debian/watch file.
++ * Build-Depends agains libasound2-dev (>= 1.0.11).
++
++ -- Julien Louis <ptitlouis@sysif.net> Wed, 23 Aug 2006 03:34:04 +0200
++
++cmus (2.0.3-3) unstable; urgency=low
++
++ * Drop libasound2-dev Build Dependency on non-linux arches
++ (Closes: #377885).
++
++ -- Julien Louis <ptitlouis@sysif.net> Wed, 12 Jul 2006 01:06:02 +0200
++
++cmus (2.0.3-2) unstable; urgency=low
++
++ * Move all dh_* helper stuff in the binary-arch target (Closes: #376320).
++ * Remove debian/patches/01_asciidoc_xsl_path.dpatch since it is not usefull.
++ * Remove dpatch from Build-Depends.
++
++ -- Julien Louis <ptitlouis@sysif.net> Sun, 2 Jul 2006 14:16:03 +0200
++
++cmus (2.0.3-1) unstable; urgency=low
++
++ * New upstream release.
++ * Remove asciidoc, docbook-xsl, doxbook-xml ans xsltproc from Build-Depends.
++ * Add libartsc0-dev and libao-dev to Build-Depends.
++
++ -- Julien Louis <ptitlouis@sysif.net> Sun, 18 Jun 2006 17:26:44 +0200
++
++cmus (2.0.2-1) unstable; urgency=low
++
++ * New upstream release.
++ * Remove ASCIIDOC patch since upstream now search for
++ /etc/asciidoc/docbook-xsl/.
++ * Add docbook-xml to Build-Depends.
++ * Bump Standards-Version no change needed.
++ * Added REAMDE.Debian
++
++ -- Julien Louis <ptitlouis@sysif.net> Tue, 30 May 2006 22:12:01 +0200
++
++cmus (2.0.0-1) unstable; urgency=low
++
++ * Initial release (Closes: #340000)
++
++ -- Julien Louis <ptitlouis@sysif.net> Mon, 7 Nov 2005 18:19:55 +0100
--- /dev/null
--- /dev/null
++contrib/cmus.bash-completion cmus
--- /dev/null
--- /dev/null
++usr
++contrib/_cmus usr/share/zsh/vendor-completions
--- /dev/null
--- /dev/null
++cmus: hardening-no-fortify-functions usr/lib/cmus/ip/flac.so
++
++## This library actually doesn't directly call any libc function
++cmus: library-not-linked-against-libc [usr/lib/cmus/op/sndio.so]
--- /dev/null
--- /dev/null
++Source: cmus
++Section: sound
++Priority: optional
++Maintainer: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
++Uploaders:
++ Ryan Kavanagh <rak@debian.org>
++Build-Depends:
++ debhelper-compat (= 13),
++ bash-completion,
++ libao-dev,
++ libasound2-dev [linux-any],
++ libavcodec-dev,
++ libavformat-dev,
++ libswresample-dev,
++ libcddb2-dev,
++ libcdio-cdda-dev,
++ libcue-dev,
++ libdiscid-dev,
++ libfaad-dev,
++ libflac-dev,
++ libjack-dev,
++ libmad0-dev,
++ libmpcdec-dev,
++ libncurses-dev,
++ libopenmpt-modplug-dev,
++ libopusfile-dev,
++ libpulse-dev,
++ libsamplerate0-dev,
++ libsystemd-dev,
++ libvorbis-dev,
++ libwavpack-dev,
++ pkgconf
++Standards-Version: 4.7.2
++Homepage: https://cmus.github.io/
++Vcs-Git: https://salsa.debian.org/multimedia-team/cmus.git
++Vcs-Browser: https://salsa.debian.org/multimedia-team/cmus
++Rules-Requires-Root: no
++
++Package: cmus
++Architecture: any
++Depends:
++ ${misc:Depends},
++ ${shlibs:Depends}
++Recommends:
++ cmus-plugin-ffmpeg
++Suggests:
++ ${shlibs:Suggests}
++Breaks: cmus-plugin-ffmpeg (<< 2.8.0)
++Description: lightweight ncurses audio player
++ C* Music Player is a modular and very configurable ncurses-based audio player.
++ It has some interesting features like configurable colorscheme, mp3 and ogg
++ streaming, it can be controlled with an UNIX socket, filters, album/artists
++ sorting and a vi-like configuration interface.
++ .
++ It currently supports different input formats:
++ - Ogg Vorbis
++ - MP3 (with libmad)
++ - FLAC
++ - Wav
++ - Modules (with libmodplug)
++ - Musepack
++ - AAC
++ - Windows Media Audio
++
++Package: cmus-plugin-ffmpeg
++Architecture: any
++Depends:
++ ${misc:Depends},
++ ${shlibs:Depends},
++ cmus (= ${binary:Version})
++Description: lightweight ncurses audio player (FFmpeg plugin)
++ C* Music Player is a modular and very configurable ncurses-based audio player.
++ It has some interesting features like configurable colorscheme, mp3 and ogg
++ streaming, it can be controlled with an UNIX socket, filters, album/artists
++ sorting and a vi-like configuration interface.
++ .
++ This package adds FFmpeg support to C* Music Player.
--- /dev/null
--- /dev/null
++Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
++Source: https://github.com/cmus/cmus
++
++Files: *
++Copyright: 2004-2007 Timo Hirvonen <tihirvon@gmail.com>
++ 2009 Gregory Petrosyan <gregory.petrosyan@gmail.com>
++ 1999 Paul N. Fisher <rao@gnu.org>
++ 2002 Andy Lo A Foe <andy@alsaplayer.org>
++ 2006 dnk <dnk@bjum.net>
++ 2006 Chun-Yu Shei <cshei AT cs.indiana.edu>
++ 2007 Kevin Ko <kevin.s.ko@gmail.com>
++ 2007 dnk <dnk@bjum.net>
++ 2007 Johannes Weißl
++ 2016 Nic Soudée
++License: GPL-2+
++
++Files: debian/*
++Copyright: 2005-2008 Julien Louis <ptitlouis@sysif.net>
++ 2010-2019 Ryan Kavanagh <ryanakca@kubuntu.org>
++ 2010 Alessio Treglia <alessio@debian.org>
++License: GPL-2+
++
++License: GPL-2+
++ This package is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++ .
++ This package is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++ .
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <https://www.gnu.org/licenses/>.
++ .
++ On Debian systems, the full text of the GNU General Public License
++ version 2 can be found in `/usr/share/common-licenses/GPL-2'.
--- /dev/null
--- /dev/null
++README.md
++contrib/
--- /dev/null
--- /dev/null
++cmus-status-display
--- /dev/null
--- /dev/null
++[DEFAULT]
++pristine-tar = True
--- /dev/null
--- /dev/null
++From: Ryan Kavanagh <rak@debian.org>
++Date: Thu, 27 Jul 2023 20:59:47 +0200
++Subject: Pass LDLIBS to the linker
++
++Origin: Debian
++Bug-Debian: http://bugs.debian.org/935678
++Forwarded: no
++Reviewed-by: Ryan Kavanagh <rak@debian.org>
++Last-Update: 2019-09-07
++
++Needed to pass -latomic at the end so that we can fix a FTBFS on various
++architectures.
++Last-Update: 2019-09-07
++---
++ Makefile | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/Makefile b/Makefile
++index 76a4d59..720f043 100644
++--- a/Makefile
+++++ b/Makefile
++@@ -19,7 +19,7 @@ include scripts/lib.mk
++ CFLAGS += -D_FILE_OFFSET_BITS=64
++
++ CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) \
++- -lm $(COMPAT_LIBS) $(LIBSYSTEMD_LIBS)
+++ -lm $(COMPAT_LIBS) $(LIBSYSTEMD_LIBS) $(LDLIBS)
++
++ command_mode.o input.o main.o ui_curses.o op/pulse.lo: .version
++ command_mode.o input.o main.o ui_curses.o op/pulse.lo: CFLAGS += -DVERSION=\"$(VERSION)\"
--- /dev/null
--- /dev/null
++Description: Use hardening flags for Doc/ttman
++Author: Philippe SWARTVAGHER <phil.swart@gmx.fr>
++Forwarded: not-needed
++Last-Update: 2025-03-16
++---
++diff --git a/Makefile b/Makefile
++index 76a4d59..3860f77 100644
++--- a/Makefile
+++++ b/Makefile
++@@ -255,12 +255,6 @@ $(man7): Doc/ttman
++ %.7: %.txt
++ $(call cmd,ttman)
++
++-Doc/ttman.o: Doc/ttman.c
++- $(call cmd,hostcc,)
++-
++-Doc/ttman: Doc/ttman.o
++- $(call cmd,hostld,)
++-
++ quiet_cmd_ttman = MAN $@
++ cmd_ttman = Doc/ttman $< $@
++ # }}}
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Thu, 14 Aug 2025 12:44:10 +0300
++Subject: ip/ffmpeg: more precise seeking
++
++av_seek_frame() and avformat_seek_file() seek to nearest "keyframe". For
++codecs like, for example, ape this means that seeking will be very off
++(5 seconds or more). So what we do is:
++1. seek to nearest "keyframe" before the desired time,
++2. discard some frames to approach the desired time.
++---
++ ip/ffmpeg.c | 154 +++++++++++++++++++++++++++++++++++++-----------------------
++ 1 file changed, 94 insertions(+), 60 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 21b9a01..ecbf005 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -44,6 +44,8 @@ struct ffmpeg_input {
++ AVPacket pkt;
++ int curr_pkt_size;
++ uint8_t *curr_pkt_buf;
+++ int64_t seek_ts;
+++ int64_t prev_frame_end;
++ int stream_index;
++
++ unsigned long curr_size;
++@@ -76,6 +78,8 @@ static struct ffmpeg_input *ffmpeg_input_create(void)
++ return NULL;
++ }
++ input->curr_pkt_size = 0;
+++ input->seek_ts = -1;
+++ input->prev_frame_end = -1;
++ input->curr_pkt_buf = input->pkt.data;
++ return input;
++ }
++@@ -314,10 +318,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ #else
++ AVFrame *frame = avcodec_alloc_frame();
++ #endif
++- int got_frame;
++ while (1) {
++- int len;
++-
++ if (input->curr_pkt_size <= 0) {
++ #if LIBAVCODEC_VERSION_MAJOR >= 56
++ av_packet_unref(&input->pkt);
++@@ -333,78 +334,108 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ #endif
++ return 0;
++ }
++- if (input->pkt.stream_index == input->stream_index) {
++- input->curr_pkt_size = input->pkt.size;
++- input->curr_pkt_buf = input->pkt.data;
++- input->curr_size += input->pkt.size;
++- input->curr_duration += input->pkt.duration;
++- }
++- continue;
++- }
++
++- {
++- AVPacket avpkt;
++- av_new_packet(&avpkt, input->curr_pkt_size);
++- memcpy(avpkt.data, input->curr_pkt_buf, input->curr_pkt_size);
+++ if (input->pkt.stream_index != input->stream_index)
+++ continue;
+++ input->curr_pkt_size = input->pkt.size;
+++ input->curr_pkt_buf = input->pkt.data;
+++ input->curr_size += input->pkt.size;
+++ input->curr_duration += input->pkt.duration;
+++
++ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++- int send_result = avcodec_send_packet(cc, &avpkt);
++- if (send_result != 0) {
++- if (send_result != AVERROR(EAGAIN)) {
++- d_print("avcodec_send_packet() returned %d\n", send_result);
++- char errstr[AV_ERROR_MAX_STRING_SIZE];
++- if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE ))
++- {
++- d_print("av_strerror(): %s\n", errstr);
++- } else {
++- d_print("av_strerror(): Description for error cannot be found\n");
++- }
++- av_packet_unref(&avpkt);
++- return -IP_ERROR_INTERNAL;
+++ int send_result = avcodec_send_packet(cc, &input->pkt);
+++ if (send_result != 0 && send_result != AVERROR(EAGAIN)) {
+++ d_print("avcodec_send_packet() returned %d\n", send_result);
+++ char errstr[AV_ERROR_MAX_STRING_SIZE];
+++ if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE ))
+++ {
+++ d_print("av_strerror(): %s\n", errstr);
+++ } else {
+++ d_print("av_strerror(): Description for error cannot be found\n");
++ }
++- len = 0;
++- } else {
++- len = input->curr_pkt_size;
+++ return -IP_ERROR_INTERNAL;
++ }
++-
++- int recv_result = avcodec_receive_frame(cc, frame);
++- got_frame = (recv_result == 0) ? 1 : 0;
++-#else
++- len = avcodec_decode_audio4(cc, frame, &got_frame, &avpkt);
++-#endif
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++- av_packet_unref(&avpkt);
++-#else
++- av_free_packet(&avpkt);
++ #endif
++ }
+++
+++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+++ int recv_result = avcodec_receive_frame(cc, frame);
+++ if (recv_result < 0) {
+++ input->curr_pkt_size = 0;
+++ continue;
+++ }
+++#else
+++ int got_frame;
+++ int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt);
++ if (len < 0) {
++ /* this is often reached when seeking, not sure why */
++ input->curr_pkt_size = 0;
++ continue;
++ }
++- input->curr_pkt_size -= len;
++- input->curr_pkt_buf += len;
++- if (got_frame) {
++- int res = swr_convert(swr,
++- &output->buffer,
++- frame->nb_samples,
++- (const uint8_t **)frame->extended_data,
++- frame->nb_samples);
++- if (res < 0)
++- res = 0;
++- output->buffer_pos = output->buffer;
+++ if (!got_frame)
+++ continue;
+++#endif
+++
+++ int64_t frame_ts = -1;
+++ if (frame->pts)
+++ frame_ts = frame->pts;
+++ else if (frame->pkt_pts)
+++ frame_ts = frame->pkt_pts;
+++ else if (frame->pkt_dts)
+++ frame_ts = frame->pkt_dts;
+++
+++ const uint8_t **in = (const uint8_t **)frame->extended_data;
+++ int in_count = frame->nb_samples;
+++ if (input->seek_ts > 0 && (frame_ts >= 0 || input->prev_frame_end >= 0)) {
+++ struct ffmpeg_private *priv = ip_data->private;
+++ AVStream *st = priv->input_context->streams[priv->input->stream_index];
+++ if (frame_ts >= 0)
+++ frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q);
+++ else
+++ frame_ts = input->prev_frame_end;
+++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
+++ int64_t frame_end = frame_ts + frame_dur;
+++ input->prev_frame_end = frame_end;
+++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
+++ if (frame_end <= input->seek_ts)
+++ continue;
+++
+++ /* skip part of this frame */
+++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
+++ in_count -= skip_samples;
+++ if (av_sample_fmt_is_planar(frame->format)) {
+++ for (int i = 0; i < cc->channels; i++) {
+++ in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
+++ }
+++ } else {
+++ *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
+++ }
+++
+++ input->seek_ts = -1;
+++ input->prev_frame_end = -1;
+++ }
+++
+++ int res = swr_convert(swr,
+++ &output->buffer,
+++ frame->nb_samples,
+++ in,
+++ in_count);
+++ if (res < 0)
+++ res = 0;
+++
+++ output->buffer_pos = output->buffer;
++ #if LIBAVCODEC_VERSION_MAJOR >= 60
++- output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
+++ output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
++ #else
++- output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
+++ output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
++ #endif
+++
++ #if LIBAVCODEC_VERSION_MAJOR >= 56
++- av_frame_free(&frame);
+++ av_frame_free(&frame);
++ #else
++- avcodec_free_frame(&frame);
+++ avcodec_free_frame(&frame);
++ #endif
++- return output->buffer_used_len;
++- }
+++ return output->buffer_used_len;
++ }
++ /* This should never get here. */
++ return -IP_ERROR_INTERNAL;
++@@ -437,13 +468,16 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++ AVStream *st = priv->input_context->streams[priv->input->stream_index];
++ int ret;
++
++- int64_t pts = av_rescale_q(offset * AV_TIME_BASE, AV_TIME_BASE_Q, st->time_base);
+++ priv->input->seek_ts = offset * AV_TIME_BASE;
+++ priv->input->prev_frame_end = -1;
+++ int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
++
++ avcodec_flush_buffers(priv->codec_context);
++ /* Force reading a new packet in next ffmpeg_fill_buffer(). */
++ priv->input->curr_pkt_size = 0;
++
++- ret = av_seek_frame(priv->input_context, priv->input->stream_index, pts, 0);
+++ ret = avformat_seek_file(priv->input_context,
+++ priv->input->stream_index, 0, ts, ts, 0);
++
++ if (ret < 0) {
++ return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Fri, 15 Aug 2025 21:42:19 +0300
++Subject: ip/ffmpeg: skip samples only when needed
++
++---
++ ip/ffmpeg.c | 32 ++++++++++++++++++--------------
++ 1 file changed, 18 insertions(+), 14 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index ecbf005..5f5a4f3 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -393,22 +393,26 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q);
++ else
++ frame_ts = input->prev_frame_end;
++- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
++- int64_t frame_end = frame_ts + frame_dur;
++- input->prev_frame_end = frame_end;
++- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
++- if (frame_end <= input->seek_ts)
++- continue;
++
++- /* skip part of this frame */
++- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
++- in_count -= skip_samples;
++- if (av_sample_fmt_is_planar(frame->format)) {
++- for (int i = 0; i < cc->channels; i++) {
++- in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
+++ if (frame_ts < input->seek_ts) {
+++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
+++ int64_t frame_end = frame_ts + frame_dur;
+++ input->prev_frame_end = frame_end;
+++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
+++ if (frame_end <= input->seek_ts)
+++ continue;
+++
+++ /* skip part of this frame */
+++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
+++ in_count -= skip_samples;
+++ if (av_sample_fmt_is_planar(frame->format)) {
+++ for (int i = 0; i < cc->channels; i++) {
+++ in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
+++ }
+++ } else {
+++ *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
++ }
++- } else {
++- *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
+++ d_print("skipping %ld samples\n", skip_samples);
++ }
++
++ input->seek_ts = -1;
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sat, 16 Aug 2025 02:43:55 +0300
++Subject: ip/ffmpeg: remove excessive version checks
++
++ffmpeg download page states that v4.0.6 has
++- libavutil 56.14.100
++- libavcodec 58.18.100
++- libavformat 58.12.100
++(https://ffmpeg.org/olddownload.html)
++
++After removing all checks for versions lower than these, the plugin
++still compiles with v3.3.9 headers.
++
++After all, why be better with compatibility than developers themselves?
++---
++ ip/ffmpeg.c | 109 +++++++++++++-----------------------------------------------
++ 1 file changed, 23 insertions(+), 86 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 5f5a4f3..f6a11f4 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -25,7 +25,6 @@
++ #include "../config/ffmpeg.h"
++ #endif
++
++-#include <stdio.h>
++ #include <libavcodec/avcodec.h>
++ #include <libavformat/avformat.h>
++ #include <libavformat/avio.h>
++@@ -43,7 +42,6 @@
++ struct ffmpeg_input {
++ AVPacket pkt;
++ int curr_pkt_size;
++- uint8_t *curr_pkt_buf;
++ int64_t seek_ts;
++ int64_t prev_frame_end;
++ int stream_index;
++@@ -80,17 +78,12 @@ static struct ffmpeg_input *ffmpeg_input_create(void)
++ input->curr_pkt_size = 0;
++ input->seek_ts = -1;
++ input->prev_frame_end = -1;
++- input->curr_pkt_buf = input->pkt.data;
++ return input;
++ }
++
++ static void ffmpeg_input_free(struct ffmpeg_input *input)
++ {
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++ av_packet_unref(&input->pkt);
++-#else
++- av_free_packet(&input->pkt);
++-#endif
++ free(input);
++ }
++
++@@ -132,7 +125,7 @@ static void ffmpeg_init(void)
++
++ av_log_set_level(AV_LOG_QUIET);
++
++-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100)
+++#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
++ /* We could register decoders explicitly to save memory, but we have to
++ * be careful about compatibility. */
++ av_register_all();
++@@ -149,9 +142,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ AVCodec const *codec;
++ AVCodecContext *cc = NULL;
++ AVFormatContext *ic = NULL;
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ AVCodecParameters *cp = NULL;
++-#endif
++ SwrContext *swr = NULL;
++
++ ffmpeg_init();
++@@ -171,20 +162,11 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ }
++
++ for (i = 0; i < ic->nb_streams; i++) {
++-
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ cp = ic->streams[i]->codecpar;
++ if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
++ stream_index = i;
++ break;
++ }
++-#else
++- cc = ic->streams[i]->codec;
++- if (cc->codec_type == AVMEDIA_TYPE_AUDIO) {
++- stream_index = i;
++- break;
++- }
++-#endif
++ }
++
++ if (stream_index == -1) {
++@@ -193,13 +175,9 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ break;
++ }
++
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ codec = avcodec_find_decoder(cp->codec_id);
++ cc = avcodec_alloc_context3(codec);
++ avcodec_parameters_to_context(cc, cp);
++-#else
++- codec = avcodec_find_decoder(cc->codec_id);
++-#endif
++ if (!codec) {
++ d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
++ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
++@@ -217,9 +195,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++
++ if (err < 0) {
++ /* Clean up. cc is never opened at this point. (See above assumption.) */
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ avcodec_free_context(&cc);
++-#endif
++ avformat_close_input(&ic);
++ return err;
++ }
++@@ -231,9 +207,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ priv->input = ffmpeg_input_create();
++ if (priv->input == NULL) {
++ avcodec_close(cc);
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ avcodec_free_context(&cc);
++-#endif
++ avformat_close_input(&ic);
++ free(priv);
++ return -IP_ERROR_INTERNAL;
++@@ -244,7 +218,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ /* Prepare for resampling. */
++ out_sample_rate = min_u(cc->sample_rate, 384000);
++ swr = swr_alloc();
++-#if LIBAVCODEC_VERSION_MAJOR >= 60
+++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++ if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
++ av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels);
++ av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0);
++@@ -259,7 +233,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ priv->swr = swr;
++
++ ip_data->private = priv;
++-#if LIBAVCODEC_VERSION_MAJOR >= 60
+++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++ ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels);
++ #else
++ ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels);
++@@ -281,10 +255,12 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ }
++ swr_init(swr);
++ ip_data->sf |= sf_host_endian();
++-#if LIBAVCODEC_VERSION_MAJOR >= 60
++- channel_map_init_waveex(cc->ch_layout.nb_channels, cc->ch_layout.u.mask, ip_data->channel_map);
+++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+++ channel_map_init_waveex(cc->ch_layout.nb_channels,
+++ cc->ch_layout.u.mask, ip_data->channel_map);
++ #else
++- channel_map_init_waveex(cc->channels, cc->channel_layout, ip_data->channel_map);
+++ channel_map_init_waveex(cc->channels,
+++ cc->channel_layout, ip_data->channel_map);
++ #endif
++ return 0;
++ }
++@@ -294,9 +270,7 @@ static int ffmpeg_close(struct input_plugin_data *ip_data)
++ struct ffmpeg_private *priv = ip_data->private;
++
++ avcodec_close(priv->codec_context);
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ avcodec_free_context(&priv->codec_context);
++-#endif
++ avformat_close_input(&priv->input_context);
++ swr_free(&priv->swr);
++ ffmpeg_input_free(priv->input);
++@@ -310,39 +284,27 @@ static int ffmpeg_close(struct input_plugin_data *ip_data)
++ * This returns the number of bytes added to the buffer.
++ * It returns < 0 on error. 0 on EOF.
++ */
++-static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext *ic, AVCodecContext *cc,
++- struct ffmpeg_input *input, struct ffmpeg_output *output, SwrContext *swr)
+++static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data,
+++ AVFormatContext *ic, AVCodecContext *cc,
+++ struct ffmpeg_input *input, struct ffmpeg_output *output,
+++ SwrContext *swr)
++ {
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++ AVFrame *frame = av_frame_alloc();
++-#else
++- AVFrame *frame = avcodec_alloc_frame();
++-#endif
++ while (1) {
++ if (input->curr_pkt_size <= 0) {
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++ av_packet_unref(&input->pkt);
++-#else
++- av_free_packet(&input->pkt);
++-#endif
++ if (av_read_frame(ic, &input->pkt) < 0) {
++ /* Force EOF once we can read no longer. */
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++ av_frame_free(&frame);
++-#else
++- avcodec_free_frame(&frame);
++-#endif
++ return 0;
++ }
++
++ if (input->pkt.stream_index != input->stream_index)
++ continue;
++ input->curr_pkt_size = input->pkt.size;
++- input->curr_pkt_buf = input->pkt.data;
++ input->curr_size += input->pkt.size;
++ input->curr_duration += input->pkt.duration;
++
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ int send_result = avcodec_send_packet(cc, &input->pkt);
++ if (send_result != 0 && send_result != AVERROR(EAGAIN)) {
++ d_print("avcodec_send_packet() returned %d\n", send_result);
++@@ -355,32 +317,17 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ }
++ return -IP_ERROR_INTERNAL;
++ }
++-#endif
++ }
++
++-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
++ int recv_result = avcodec_receive_frame(cc, frame);
++ if (recv_result < 0) {
++ input->curr_pkt_size = 0;
++ continue;
++ }
++-#else
++- int got_frame;
++- int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt);
++- if (len < 0) {
++- /* this is often reached when seeking, not sure why */
++- input->curr_pkt_size = 0;
++- continue;
++- }
++- if (!got_frame)
++- continue;
++-#endif
++
++ int64_t frame_ts = -1;
++ if (frame->pts)
++ frame_ts = frame->pts;
++- else if (frame->pkt_pts)
++- frame_ts = frame->pkt_pts;
++ else if (frame->pkt_dts)
++ frame_ts = frame->pkt_dts;
++
++@@ -395,7 +342,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ frame_ts = input->prev_frame_end;
++
++ if (frame_ts < input->seek_ts) {
++- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf));
+++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, frame->sample_rate);
++ int64_t frame_end = frame_ts + frame_dur;
++ input->prev_frame_end = frame_end;
++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
++@@ -403,14 +350,14 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ continue;
++
++ /* skip part of this frame */
++- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE);
+++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, frame->sample_rate, AV_TIME_BASE);
++ in_count -= skip_samples;
++ if (av_sample_fmt_is_planar(frame->format)) {
++- for (int i = 0; i < cc->channels; i++) {
+++ for (int i = 0; i < sf_get_channels(ip_data->sf); i++) {
++ in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
++ }
++ } else {
++- *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf);
+++ *in += skip_samples * sf_get_frame_size(ip_data->sf);
++ }
++ d_print("skipping %ld samples\n", skip_samples);
++ }
++@@ -428,17 +375,9 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext
++ res = 0;
++
++ output->buffer_pos = output->buffer;
++-#if LIBAVCODEC_VERSION_MAJOR >= 60
++- output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf);
++-#else
++- output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf);
++-#endif
+++ output->buffer_used_len = res * sf_get_frame_size(ip_data->sf);
++
++-#if LIBAVCODEC_VERSION_MAJOR >= 56
++ av_frame_free(&frame);
++-#else
++- avcodec_free_frame(&frame);
++-#endif
++ return output->buffer_used_len;
++ }
++ /* This should never get here. */
++@@ -453,11 +392,11 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun
++ int out_size;
++
++ if (output->buffer_used_len == 0) {
++- rc = ffmpeg_fill_buffer(ip_data, priv->input_context, priv->codec_context,
+++ rc = ffmpeg_fill_buffer(ip_data,
+++ priv->input_context, priv->codec_context,
++ priv->input, priv->output, priv->swr);
++- if (rc <= 0) {
+++ if (rc <= 0)
++ return rc;
++- }
++ }
++ out_size = min_i(output->buffer_used_len, count);
++ memcpy(buffer, output->buffer_pos, out_size);
++@@ -477,6 +416,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++ int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
++
++ avcodec_flush_buffers(priv->codec_context);
+++ /* TODO: also flush swresample buffers */
++ /* Force reading a new packet in next ffmpeg_fill_buffer(). */
++ priv->input->curr_pkt_size = 0;
++
++@@ -501,7 +441,8 @@ static void ffmpeg_read_metadata(struct growing_keyvals *c, AVDictionary *metada
++ }
++ }
++
++-static int ffmpeg_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
+++static int ffmpeg_read_comments(struct input_plugin_data *ip_data,
+++ struct keyval **comments)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++ AVFormatContext *ic = priv->input_context;
++@@ -538,11 +479,7 @@ static long ffmpeg_current_bitrate(struct input_plugin_data *ip_data)
++ AVStream *st = priv->input_context->streams[priv->input->stream_index];
++ long bitrate = -1;
++ /* ape codec returns silly numbers */
++-#if LIBAVCODEC_VERSION_MAJOR >= 55
++ if (priv->codec->id == AV_CODEC_ID_APE)
++-#else
++- if (priv->codec->id == CODEC_ID_APE)
++-#endif
++ return -1;
++ if (priv->input->curr_duration > 0) {
++ double seconds = priv->input->curr_duration * av_q2d(st->time_base);
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 04:05:36 +0300
++Subject: ip/ffmpeg: major refactor
++
++---
++ ip/ffmpeg.c | 643 +++++++++++++++++++++++++++++++-----------------------------
++ 1 file changed, 330 insertions(+), 313 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index f6a11f4..42f630e 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -35,84 +35,32 @@
++ #include <libavutil/mathematics.h>
++ #endif
++
++-#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
++-#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
++-#endif
+++struct ffmpeg_private {
+++ AVCodecContext *codec_ctx;
+++ AVFormatContext *format_ctx;
+++ AVCodec const *codec;
+++ SwrContext *swr;
+++ int stream_index;
++
++-struct ffmpeg_input {
++- AVPacket pkt;
++- int curr_pkt_size;
+++ AVPacket *pkt;
+++ AVFrame *frame;
++ int64_t seek_ts;
++ int64_t prev_frame_end;
++- int stream_index;
++
+++ /* A buffer to hold swr_convert()-ed samples */
+++ AVFrame *swr_frame;
+++ int swr_frame_start;
+++
+++ /* Bitrate estimation */
++ unsigned long curr_size;
++ unsigned long curr_duration;
++ };
++
++-struct ffmpeg_output {
++- uint8_t *buffer;
++- uint8_t *buffer_malloc;
++- uint8_t *buffer_pos; /* current buffer position */
++- int buffer_used_len;
++-};
++-
++-struct ffmpeg_private {
++- AVCodecContext *codec_context;
++- AVFormatContext *input_context;
++- AVCodec const *codec;
++- SwrContext *swr;
++-
++- struct ffmpeg_input *input;
++- struct ffmpeg_output *output;
++-};
++-
++-static struct ffmpeg_input *ffmpeg_input_create(void)
++-{
++- struct ffmpeg_input *input = xnew(struct ffmpeg_input, 1);
++-
++- if (av_new_packet(&input->pkt, 0) != 0) {
++- free(input);
++- return NULL;
++- }
++- input->curr_pkt_size = 0;
++- input->seek_ts = -1;
++- input->prev_frame_end = -1;
++- return input;
++-}
++-
++-static void ffmpeg_input_free(struct ffmpeg_input *input)
++-{
++- av_packet_unref(&input->pkt);
++- free(input);
++-}
++-
++-static struct ffmpeg_output *ffmpeg_output_create(void)
++-{
++- struct ffmpeg_output *output = xnew(struct ffmpeg_output, 1);
++-
++- output->buffer_malloc = xnew(uint8_t, AVCODEC_MAX_AUDIO_FRAME_SIZE + 15);
++- output->buffer = output->buffer_malloc;
++- /* align to 16 bytes so avcodec can SSE/Altivec/etc */
++- while ((intptr_t) output->buffer % 16)
++- output->buffer += 1;
++- output->buffer_pos = output->buffer;
++- output->buffer_used_len = 0;
++- return output;
++-}
++-
++-static void ffmpeg_output_free(struct ffmpeg_output *output)
++-{
++- free(output->buffer_malloc);
++- output->buffer_malloc = NULL;
++- output->buffer = NULL;
++- free(output);
++-}
++-
++-static inline void ffmpeg_buffer_flush(struct ffmpeg_output *output)
+++static const char *ffmpeg_errmsg(int err)
++ {
++- output->buffer_pos = output->buffer;
++- output->buffer_used_len = 0;
+++ static char errstr[AV_ERROR_MAX_STRING_SIZE];
+++ av_strerror(err, errstr, AV_ERROR_MAX_STRING_SIZE);
+++ return errstr;
++ }
++
++ static void ffmpeg_init(void)
++@@ -132,303 +80,372 @@ static void ffmpeg_init(void)
++ #endif
++ }
++
++-static int ffmpeg_open(struct input_plugin_data *ip_data)
+++static int ffmpeg_open_input(struct input_plugin_data *ip_data,
+++ struct ffmpeg_private *priv)
++ {
++- struct ffmpeg_private *priv;
++- int err = 0;
++- int i;
++- int stream_index = -1;
++- int out_sample_rate;
++- AVCodec const *codec;
++- AVCodecContext *cc = NULL;
++ AVFormatContext *ic = NULL;
+++ AVCodecContext *cc = NULL;
++ AVCodecParameters *cp = NULL;
++- SwrContext *swr = NULL;
++-
++- ffmpeg_init();
+++ AVCodec const *codec = NULL;
+++ int stream_index = -1;
++
++- err = avformat_open_input(&ic, ip_data->filename, NULL, NULL);
++- if (err < 0) {
++- d_print("av_open failed: %d\n", err);
++- return -IP_ERROR_FILE_FORMAT;
+++ int err;
+++ int res = avformat_open_input(&ic, ip_data->filename, NULL, NULL);
+++ if (res < 0) {
+++ err = -IP_ERROR_FILE_FORMAT;
+++ goto err;
++ }
++
++- do {
++- err = avformat_find_stream_info(ic, NULL);
++- if (err < 0) {
++- d_print("unable to find stream info: %d\n", err);
++- err = -IP_ERROR_FILE_FORMAT;
++- break;
++- }
++-
++- for (i = 0; i < ic->nb_streams; i++) {
++- cp = ic->streams[i]->codecpar;
++- if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
++- stream_index = i;
++- break;
++- }
++- }
++-
++- if (stream_index == -1) {
++- d_print("could not find audio stream\n");
++- err = -IP_ERROR_FILE_FORMAT;
++- break;
++- }
++-
++- codec = avcodec_find_decoder(cp->codec_id);
++- cc = avcodec_alloc_context3(codec);
++- avcodec_parameters_to_context(cc, cp);
++- if (!codec) {
++- d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
++- err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
++- break;
++- }
+++ res = avformat_find_stream_info(ic, NULL);
+++ if (res < 0) {
+++ d_print("unable to find stream info\n");
+++ err = -IP_ERROR_FILE_FORMAT;
+++ goto err;
+++ }
++
++- if (avcodec_open2(cc, codec, NULL) < 0) {
++- d_print("could not open codec: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id));
++- err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
+++ for (int i = 0; i < ic->nb_streams; i++) {
+++ cp = ic->streams[i]->codecpar;
+++ if (cp->codec_type == AVMEDIA_TYPE_AUDIO) {
+++ stream_index = i;
++ break;
++ }
+++ }
++
++- /* We assume below that no more errors follow. */
++- } while (0);
+++ if (stream_index == -1) {
+++ d_print("could not find audio stream\n");
+++ err = -IP_ERROR_FILE_FORMAT;
+++ goto err_silent;
+++ }
++
++- if (err < 0) {
++- /* Clean up. cc is never opened at this point. (See above assumption.) */
++- avcodec_free_context(&cc);
++- avformat_close_input(&ic);
++- return err;
+++ codec = avcodec_find_decoder(cp->codec_id);
+++ if (!codec) {
+++ d_print("codec (id: %d, name: %s) not found\n",
+++ cc->codec_id, avcodec_get_name(cc->codec_id));
+++ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
+++ goto err_silent;
+++ }
+++ cc = avcodec_alloc_context3(codec);
+++ avcodec_parameters_to_context(cc, cp);
+++
+++ res = avcodec_open2(cc, codec, NULL);
+++ if (res < 0) {
+++ d_print("could not open codec (id: %d, name: %s)\n",
+++ cc->codec_id, avcodec_get_name(cc->codec_id));
+++ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE;
+++ goto err;
++ }
++
++- priv = xnew(struct ffmpeg_private, 1);
++- priv->codec_context = cc;
++- priv->input_context = ic;
+++ priv->format_ctx = ic;
+++ priv->codec_ctx = cc;
++ priv->codec = codec;
++- priv->input = ffmpeg_input_create();
++- if (priv->input == NULL) {
++- avcodec_close(cc);
++- avcodec_free_context(&cc);
++- avformat_close_input(&ic);
++- free(priv);
++- return -IP_ERROR_INTERNAL;
+++ priv->stream_index = stream_index;
+++ return 0;
+++err:
+++ d_print("%s\n", ffmpeg_errmsg(res));
+++err_silent:
+++ avcodec_free_context(&cc);
+++ avformat_close_input(&ic);
+++ return err;
+++}
+++
+++static void ffmpeg_set_sf_and_swr_opts(SwrContext *swr, AVCodecContext *cc,
+++ sample_format_t *sf_out, enum AVSampleFormat *out_sample_fmt)
+++{
+++ int out_sample_rate = min_u(cc->sample_rate, 384000);
+++ sample_format_t sf = sf_rate(out_sample_rate) | sf_host_endian();
+++ av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0);
+++ av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
+++
+++ *out_sample_fmt = cc->sample_fmt;
+++ switch (*out_sample_fmt) {
+++ case AV_SAMPLE_FMT_U8:
+++ sf |= sf_bits(8) | sf_signed(0);
+++ break;
+++ case AV_SAMPLE_FMT_S32:
+++ sf |= sf_bits(32) | sf_signed(1);
+++ break;
+++ default:
+++ sf |= sf_bits(16) | sf_signed(1);
+++ *out_sample_fmt = AV_SAMPLE_FMT_S16;
++ }
++- priv->input->stream_index = stream_index;
++- priv->output = ffmpeg_output_create();
+++ av_opt_set_sample_fmt(swr, "in_sample_fmt", cc->sample_fmt, 0);
+++ av_opt_set_sample_fmt(swr, "out_sample_fmt", *out_sample_fmt, 0);
++
++- /* Prepare for resampling. */
++- out_sample_rate = min_u(cc->sample_rate, 384000);
++- swr = swr_alloc();
++ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+++ sf |= sf_channels(cc->ch_layout.nb_channels);
+++
++ if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
++ av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels);
++- av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0);
++- av_opt_set_chlayout(swr, "out_chlayout", &cc->ch_layout, 0);
+++ av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0);
+++ av_opt_set_chlayout(swr, "out_chlayout", &cc->ch_layout, 0);
++ #else
++- av_opt_set_int(swr, "in_channel_layout", av_get_default_channel_layout(cc->channels), 0);
++- av_opt_set_int(swr, "out_channel_layout", av_get_default_channel_layout(cc->channels), 0);
+++ sf |= sf_channels(cc->channels);
+++
+++ av_opt_set_int(swr, "in_channel_layout",
+++ av_get_default_channel_layout(cc->channels), 0);
+++ av_opt_set_int(swr, "out_channel_layout",
+++ av_get_default_channel_layout(cc->channels), 0);
++ #endif
++- av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0);
++- av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
++- av_opt_set_sample_fmt(swr, "in_sample_fmt", cc->sample_fmt, 0);
++- priv->swr = swr;
++
++- ip_data->private = priv;
+++ *sf_out = sf;
+++}
+++
+++static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv,
+++ sample_format_t sf, enum AVSampleFormat out_sample_fmt)
+++{
+++ AVCodecContext *cc = priv->codec_ctx;
+++ AVFrame *frame = av_frame_alloc();
+++
++ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++- ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels);
+++ av_channel_layout_copy(&frame->ch_layout, &cc->ch_layout);
++ #else
++- ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels);
+++ frame->channel_layout = av_get_default_channel_layout(cc->channels);
++ #endif
++- switch (cc->sample_fmt) {
++- case AV_SAMPLE_FMT_U8:
++- ip_data->sf |= sf_bits(8) | sf_signed(0);
++- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_U8, 0);
++- break;
++- case AV_SAMPLE_FMT_S32:
++- ip_data->sf |= sf_bits(32) | sf_signed(1);
++- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S32, 0);
++- break;
++- /* AV_SAMPLE_FMT_S16 */
++- default:
++- ip_data->sf |= sf_bits(16) | sf_signed(1);
++- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
++- break;
+++
+++ frame->sample_rate = sf_get_rate(sf);
+++ frame->format = out_sample_fmt;
+++
+++ /* NOTE: 10 sec is probably too much, but the amount of space
+++ * needed for swr_convert() is unpredictable */
+++ frame->nb_samples = 10 * sf_get_rate(sf);
+++ int res = av_frame_get_buffer(frame, 0);
+++ if (res < 0) {
+++ d_print("av_frame_get_buffer(): %s\n", ffmpeg_errmsg(res));
+++ return -IP_ERROR_INTERNAL;
++ }
++- swr_init(swr);
++- ip_data->sf |= sf_host_endian();
+++ frame->nb_samples = 0;
+++
+++ priv->swr_frame = frame;
+++ return 0;
+++}
+++
+++static void ffmpeg_free(struct ffmpeg_private *priv)
+++{
+++ avcodec_close(priv->codec_ctx);
+++ avcodec_free_context(&priv->codec_ctx);
+++ avformat_close_input(&priv->format_ctx);
+++
+++ swr_free(&priv->swr);
+++
+++ av_frame_free(&priv->frame);
+++ av_packet_free(&priv->pkt);
+++ av_frame_free(&priv->swr_frame);
+++}
+++
+++static int ffmpeg_open(struct input_plugin_data *ip_data)
+++{
+++ struct ffmpeg_private priv;
+++ enum AVSampleFormat out_sample_fmt;
+++ memset(&priv, 0, sizeof(struct ffmpeg_private));
+++
+++ ffmpeg_init();
+++
+++ int err = ffmpeg_open_input(ip_data, &priv);
+++ if (err < 0)
+++ return err;
+++
+++ priv.pkt = av_packet_alloc();
+++ priv.frame = av_frame_alloc();
+++ priv.seek_ts = -1;
+++ priv.prev_frame_end = -1;
+++
+++ priv.swr = swr_alloc();
+++ ffmpeg_set_sf_and_swr_opts(priv.swr, priv.codec_ctx,
+++ &ip_data->sf, &out_sample_fmt);
+++ swr_init(priv.swr);
+++
+++ err = ffmpeg_init_swr_frame(&priv, ip_data->sf, out_sample_fmt);
+++ if (err < 0) {
+++ ffmpeg_free(&priv);
+++ return err;
+++ }
+++
++ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++- channel_map_init_waveex(cc->ch_layout.nb_channels,
++- cc->ch_layout.u.mask, ip_data->channel_map);
+++ channel_map_init_waveex(priv.codec_ctx->ch_layout.nb_channels,
+++ priv.codec_ctx->ch_layout.u.mask, ip_data->channel_map);
++ #else
++- channel_map_init_waveex(cc->channels,
++- cc->channel_layout, ip_data->channel_map);
+++ channel_map_init_waveex(priv.codec_ctx->channels,
+++ priv.codec_ctx->channel_layout, ip_data->channel_map);
++ #endif
+++
+++ ip_data->private = xnew(struct ffmpeg_private, 1);
+++ memcpy(ip_data->private, &priv, sizeof(struct ffmpeg_private));
++ return 0;
++ }
++
++ static int ffmpeg_close(struct input_plugin_data *ip_data)
++ {
++- struct ffmpeg_private *priv = ip_data->private;
++-
++- avcodec_close(priv->codec_context);
++- avcodec_free_context(&priv->codec_context);
++- avformat_close_input(&priv->input_context);
++- swr_free(&priv->swr);
++- ffmpeg_input_free(priv->input);
++- ffmpeg_output_free(priv->output);
++- free(priv);
+++ ffmpeg_free(ip_data->private);
+++ free(ip_data->private);
++ ip_data->private = NULL;
++ return 0;
++ }
++
++ /*
++- * This returns the number of bytes added to the buffer.
++- * It returns < 0 on error. 0 on EOF.
+++ * return:
+++ * 0 - retry
+++ * >0 - ok
++ */
++-static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data,
++- AVFormatContext *ic, AVCodecContext *cc,
++- struct ffmpeg_input *input, struct ffmpeg_output *output,
++- SwrContext *swr)
+++static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts)
++ {
++- AVFrame *frame = av_frame_alloc();
++- while (1) {
++- if (input->curr_pkt_size <= 0) {
++- av_packet_unref(&input->pkt);
++- if (av_read_frame(ic, &input->pkt) < 0) {
++- /* Force EOF once we can read no longer. */
++- av_frame_free(&frame);
++- return 0;
++- }
++-
++- if (input->pkt.stream_index != input->stream_index)
++- continue;
++- input->curr_pkt_size = input->pkt.size;
++- input->curr_size += input->pkt.size;
++- input->curr_duration += input->pkt.duration;
++-
++- int send_result = avcodec_send_packet(cc, &input->pkt);
++- if (send_result != 0 && send_result != AVERROR(EAGAIN)) {
++- d_print("avcodec_send_packet() returned %d\n", send_result);
++- char errstr[AV_ERROR_MAX_STRING_SIZE];
++- if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE ))
++- {
++- d_print("av_strerror(): %s\n", errstr);
++- } else {
++- d_print("av_strerror(): Description for error cannot be found\n");
++- }
++- return -IP_ERROR_INTERNAL;
++- }
++- }
+++ if (frame_ts >= 0) {
+++ AVStream *s = priv->format_ctx->streams[priv->stream_index];
+++ frame_ts = av_rescale_q(frame_ts, s->time_base, AV_TIME_BASE_Q);
+++ } else {
+++ frame_ts = priv->prev_frame_end;
+++ }
++
++- int recv_result = avcodec_receive_frame(cc, frame);
++- if (recv_result < 0) {
++- input->curr_pkt_size = 0;
++- continue;
++- }
+++ if (frame_ts >= priv->seek_ts)
+++ return 1;
++
++- int64_t frame_ts = -1;
++- if (frame->pts)
++- frame_ts = frame->pts;
++- else if (frame->pkt_dts)
++- frame_ts = frame->pkt_dts;
++-
++- const uint8_t **in = (const uint8_t **)frame->extended_data;
++- int in_count = frame->nb_samples;
++- if (input->seek_ts > 0 && (frame_ts >= 0 || input->prev_frame_end >= 0)) {
++- struct ffmpeg_private *priv = ip_data->private;
++- AVStream *st = priv->input_context->streams[priv->input->stream_index];
++- if (frame_ts >= 0)
++- frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q);
++- else
++- frame_ts = input->prev_frame_end;
++-
++- if (frame_ts < input->seek_ts) {
++- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, frame->sample_rate);
++- int64_t frame_end = frame_ts + frame_dur;
++- input->prev_frame_end = frame_end;
++- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end);
++- if (frame_end <= input->seek_ts)
++- continue;
++-
++- /* skip part of this frame */
++- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, frame->sample_rate, AV_TIME_BASE);
++- in_count -= skip_samples;
++- if (av_sample_fmt_is_planar(frame->format)) {
++- for (int i = 0; i < sf_get_channels(ip_data->sf); i++) {
++- in[i] += skip_samples * sf_get_sample_size(ip_data->sf);
++- }
++- } else {
++- *in += skip_samples * sf_get_frame_size(ip_data->sf);
++- }
++- d_print("skipping %ld samples\n", skip_samples);
++- }
++-
++- input->seek_ts = -1;
++- input->prev_frame_end = -1;
++- }
+++ int64_t frame_dur = av_rescale(priv->frame->nb_samples,
+++ AV_TIME_BASE, priv->frame->sample_rate);
+++ int64_t frame_end = frame_ts + frame_dur;
+++ priv->prev_frame_end = frame_end;
+++
+++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n",
+++ priv->seek_ts, frame_ts, frame_end);
+++
+++ if (frame_end <= priv->seek_ts)
+++ return 0;
+++
+++ int64_t skip_samples = av_rescale(priv->seek_ts - frame_ts,
+++ priv->frame->sample_rate, AV_TIME_BASE);
+++ priv->frame->nb_samples -= skip_samples;
+++
+++ int bps = av_get_bytes_per_sample(priv->frame->format);
+++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
+++ int channels = priv->codec_ctx->ch_layout.nb_channels;
+++#else
+++ int channels = priv->codec_ctx->channels;
+++#endif
+++
+++ /* Just modify frame's data pointer because it's throw-away */
+++ if (av_sample_fmt_is_planar(priv->frame->format)) {
+++ for (int i = 0; i < channels; i++)
+++ priv->frame->extended_data[i] += skip_samples * bps;
+++ } else {
+++ priv->frame->extended_data[0] += skip_samples * channels * bps;
+++ }
+++ d_print("skipping %ld samples\n", skip_samples);
+++ return 1;
+++}
++
++- int res = swr_convert(swr,
++- &output->buffer,
++- frame->nb_samples,
++- in,
++- in_count);
+++/*
+++ * return:
+++ * <0 - error
+++ * 0 - retry
+++ * >0 - ok
+++ */
+++static int ffmpeg_get_frame(struct ffmpeg_private *priv)
+++{
+++ int res = avcodec_receive_frame(priv->codec_ctx, priv->frame);
+++ if (res == AVERROR(EAGAIN)) {
+++ av_packet_unref(priv->pkt);
+++ res = av_read_frame(priv->format_ctx, priv->pkt);
++ if (res < 0)
++- res = 0;
+++ return res;
+++
+++ if (priv->pkt->stream_index != priv->stream_index)
+++ return 0;
++
++- output->buffer_pos = output->buffer;
++- output->buffer_used_len = res * sf_get_frame_size(ip_data->sf);
+++ priv->curr_size += priv->pkt->size;
+++ priv->curr_duration += priv->pkt->duration;
++
++- av_frame_free(&frame);
++- return output->buffer_used_len;
+++ res = avcodec_send_packet(priv->codec_ctx, priv->pkt);
+++ if (res == AVERROR(EAGAIN))
+++ return 0;
++ }
++- /* This should never get here. */
++- return -IP_ERROR_INTERNAL;
+++ if (res < 0)
+++ return res;
+++
+++ int64_t frame_ts = -1;
+++ if (priv->frame->pts >= 0)
+++ frame_ts = priv->frame->pts;
+++ else if (priv->frame->pkt_dts >= 0)
+++ frame_ts = priv->frame->pkt_dts;
+++
+++ if (priv->seek_ts > 0 && (frame_ts >= 0 || priv->prev_frame_end >= 0)) {
+++ if (ffmpeg_seek_into_frame(priv, frame_ts) == 0)
+++ return 0;
+++ priv->seek_ts = -1;
+++ priv->prev_frame_end = -1;
+++ }
+++ return 1;
+++}
+++
+++static int ffmpeg_convert_frame(struct ffmpeg_private *priv)
+++{
+++ int res = swr_convert(priv->swr,
+++ priv->swr_frame->extended_data,
+++ /* TODO: proper buffer capacity */
+++ priv->frame->nb_samples,
+++ (const uint8_t **)priv->frame->extended_data,
+++ priv->frame->nb_samples);
+++ if (res >= 0) {
+++ priv->swr_frame->nb_samples = res;
+++ priv->swr_frame_start = 0;
+++ }
+++ return res;
++ }
++
++ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- struct ffmpeg_output *output = priv->output;
++- int rc;
++- int out_size;
++-
++- if (output->buffer_used_len == 0) {
++- rc = ffmpeg_fill_buffer(ip_data,
++- priv->input_context, priv->codec_context,
++- priv->input, priv->output, priv->swr);
++- if (rc <= 0)
++- return rc;
+++ int written = 0;
+++ int res;
+++
+++ count /= sf_get_frame_size(ip_data->sf);
+++
+++ while (count) {
+++ if (priv->swr_frame->nb_samples == 0) {
+++ res = ffmpeg_get_frame(priv);
+++ if (res == AVERROR_EOF)
+++ break;
+++ else if (res == 0)
+++ continue;
+++ else if (res < 0)
+++ goto err;
+++
+++ res = ffmpeg_convert_frame(priv);
+++ if (res < 0)
+++ goto err;
+++ }
+++
+++ int copy_frames = min_i(count, priv->swr_frame->nb_samples);
+++ int copy_bytes = copy_frames * sf_get_frame_size(ip_data->sf);
+++ void *dst = priv->swr_frame->extended_data[0] + priv->swr_frame_start;
+++ memcpy(buffer + written, dst, copy_bytes);
+++
+++ priv->swr_frame->nb_samples -= copy_frames;
+++ priv->swr_frame_start += copy_bytes;
+++ count -= copy_frames;
+++ written += copy_bytes;
++ }
++- out_size = min_i(output->buffer_used_len, count);
++- memcpy(buffer, output->buffer_pos, out_size);
++- output->buffer_used_len -= out_size;
++- output->buffer_pos += out_size;
++- return out_size;
+++ return written;
+++err:
+++ d_print("%s\n", ffmpeg_errmsg(res));
+++ return -IP_ERROR_INTERNAL;
++ }
++
++ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- AVStream *st = priv->input_context->streams[priv->input->stream_index];
++- int ret;
+++ AVStream *st = priv->format_ctx->streams[priv->stream_index];
++
++- priv->input->seek_ts = offset * AV_TIME_BASE;
++- priv->input->prev_frame_end = -1;
+++ priv->seek_ts = offset * AV_TIME_BASE;
+++ priv->prev_frame_end = -1;
++ int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
++
++- avcodec_flush_buffers(priv->codec_context);
++- /* TODO: also flush swresample buffers */
++- /* Force reading a new packet in next ffmpeg_fill_buffer(). */
++- priv->input->curr_pkt_size = 0;
++-
++- ret = avformat_seek_file(priv->input_context,
++- priv->input->stream_index, 0, ts, ts, 0);
++-
++- if (ret < 0) {
+++ int ret = avformat_seek_file(priv->format_ctx,
+++ priv->stream_index, 0, ts, ts, 0);
+++ if (ret < 0)
++ return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
++- } else {
++- ffmpeg_buffer_flush(priv->output);
++- return 0;
++- }
+++
+++ priv->swr_frame->nb_samples = 0;
+++ avcodec_flush_buffers(priv->codec_ctx);
+++ /* also flush swresample buffers? */
+++ return 0;
++ }
++
++ static void ffmpeg_read_metadata(struct growing_keyvals *c, AVDictionary *metadata)
++@@ -445,7 +462,7 @@ static int ffmpeg_read_comments(struct input_plugin_data *ip_data,
++ struct keyval **comments)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- AVFormatContext *ic = priv->input_context;
+++ AVFormatContext *ic = priv->format_ctx;
++
++ GROWING_KEYVALS(c);
++
++@@ -463,29 +480,29 @@ static int ffmpeg_read_comments(struct input_plugin_data *ip_data,
++ static int ffmpeg_duration(struct input_plugin_data *ip_data)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- return priv->input_context->duration / AV_TIME_BASE;
+++ return priv->format_ctx->duration / AV_TIME_BASE;
++ }
++
++ static long ffmpeg_bitrate(struct input_plugin_data *ip_data)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- long bitrate = priv->input_context->bit_rate;
+++ long bitrate = priv->format_ctx->bit_rate;
++ return bitrate ? bitrate : -IP_ERROR_FUNCTION_NOT_SUPPORTED;
++ }
++
++ static long ffmpeg_current_bitrate(struct input_plugin_data *ip_data)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++- AVStream *st = priv->input_context->streams[priv->input->stream_index];
+++ AVStream *st = priv->format_ctx->streams[priv->stream_index];
++ long bitrate = -1;
++ /* ape codec returns silly numbers */
++ if (priv->codec->id == AV_CODEC_ID_APE)
++ return -1;
++- if (priv->input->curr_duration > 0) {
++- double seconds = priv->input->curr_duration * av_q2d(st->time_base);
++- bitrate = (8 * priv->input->curr_size) / seconds;
++- priv->input->curr_size = 0;
++- priv->input->curr_duration = 0;
+++ if (priv->curr_duration > 0) {
+++ double seconds = priv->curr_duration * av_q2d(st->time_base);
+++ bitrate = (8 * priv->curr_size) / seconds;
+++ priv->curr_size = 0;
+++ priv->curr_duration = 0;
++ }
++ return bitrate;
++ }
++@@ -500,7 +517,7 @@ static char *ffmpeg_codec_profile(struct input_plugin_data *ip_data)
++ {
++ struct ffmpeg_private *priv = ip_data->private;
++ const char *profile;
++- profile = av_get_profile_name(priv->codec, priv->codec_context->profile);
+++ profile = av_get_profile_name(priv->codec, priv->codec_ctx->profile);
++ return profile ? xstrdup(profile) : NULL;
++ }
++
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 14:28:46 +0300
++Subject: Validate sample format in ip_open()
++
++To prevent segfault in ip_setup() because channels=0, validate ip_data->sf
++after opening ip.
++---
++ input.c | 10 ++++++++++
++ 1 file changed, 10 insertions(+)
++
++diff --git a/input.c b/input.c
++index c20cb3f..f5c5b3c 100644
++--- a/input.c
+++++ b/input.c
++@@ -605,6 +605,16 @@ int ip_open(struct input_plugin *ip)
++ ip_reset(ip, 1);
++ return rc;
++ }
+++
+++ unsigned bits = sf_get_bits(ip->data.sf);
+++ unsigned channels = sf_get_channels(ip->data.sf);
+++ unsigned rate = sf_get_rate(ip->data.sf);
+++ if (!bits || !channels || !rate) {
+++ d_print("corrupt file: bits = %u, channels = %u, rate = %u\n",
+++ bits, channels, rate);
+++ return -IP_ERROR_FILE_FORMAT;
+++ }
+++
++ ip->open = 1;
++ return 0;
++ }
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 14:53:52 +0300
++Subject: ip/ffmpeg: flush swresample buffer when seeking
++
++---
++ ip/ffmpeg.c | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 42f630e..775e7de 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -444,7 +444,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++
++ priv->swr_frame->nb_samples = 0;
++ avcodec_flush_buffers(priv->codec_ctx);
++- /* also flush swresample buffers? */
+++ swr_convert(priv->swr, NULL, 0, NULL, 0); /* flush swr buffer */
++ return 0;
++ }
++
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 15:02:34 +0300
++Subject: ip/ffmpeg: remember swr_frame's capacity
++
++---
++ ip/ffmpeg.c | 5 +++--
++ 1 file changed, 3 insertions(+), 2 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 775e7de..c659c13 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -49,6 +49,7 @@ struct ffmpeg_private {
++
++ /* A buffer to hold swr_convert()-ed samples */
++ AVFrame *swr_frame;
+++ int swr_frame_samples_cap;
++ int swr_frame_start;
++
++ /* Bitrate estimation */
++@@ -213,6 +214,7 @@ static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv,
++ d_print("av_frame_get_buffer(): %s\n", ffmpeg_errmsg(res));
++ return -IP_ERROR_INTERNAL;
++ }
+++ priv->swr_frame_samples_cap = frame->nb_samples;
++ frame->nb_samples = 0;
++
++ priv->swr_frame = frame;
++@@ -378,8 +380,7 @@ static int ffmpeg_convert_frame(struct ffmpeg_private *priv)
++ {
++ int res = swr_convert(priv->swr,
++ priv->swr_frame->extended_data,
++- /* TODO: proper buffer capacity */
++- priv->frame->nb_samples,
+++ priv->swr_frame_samples_cap,
++ (const uint8_t **)priv->frame->extended_data,
++ priv->frame->nb_samples);
++ if (res >= 0) {
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 15:54:19 +0300
++Subject: ip/ffmpeg: reset swr_frame_start when seeking
++
++---
++ ip/ffmpeg.c | 1 +
++ 1 file changed, 1 insertion(+)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index c659c13..71cc511 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -444,6 +444,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++ return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
++
++ priv->swr_frame->nb_samples = 0;
+++ priv->swr_frame_start = 0;
++ avcodec_flush_buffers(priv->codec_ctx);
++ swr_convert(priv->swr, NULL, 0, NULL, 0); /* flush swr buffer */
++ return 0;
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 17:27:20 +0300
++Subject: ip/ffmpeg: better frame skipping logic
++
++---
++ ip/ffmpeg.c | 82 ++++++++++++++++++++++++++++++-------------------------------
++ 1 file changed, 41 insertions(+), 41 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 71cc511..af6ecfb 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -44,8 +44,8 @@ struct ffmpeg_private {
++
++ AVPacket *pkt;
++ AVFrame *frame;
++- int64_t seek_ts;
++- int64_t prev_frame_end;
+++ double seek_ts;
+++ int64_t skip_samples;
++
++ /* A buffer to hold swr_convert()-ed samples */
++ AVFrame *swr_frame;
++@@ -249,7 +249,6 @@ static int ffmpeg_open(struct input_plugin_data *ip_data)
++ priv.pkt = av_packet_alloc();
++ priv.frame = av_frame_alloc();
++ priv.seek_ts = -1;
++- priv.prev_frame_end = -1;
++
++ priv.swr = swr_alloc();
++ ffmpeg_set_sf_and_swr_opts(priv.swr, priv.codec_ctx,
++@@ -283,37 +282,37 @@ static int ffmpeg_close(struct input_plugin_data *ip_data)
++ return 0;
++ }
++
++-/*
++- * return:
++- * 0 - retry
++- * >0 - ok
++- */
++-static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts)
+++static int64_t ffmpeg_calc_skip_samples(struct ffmpeg_private *priv)
++ {
++- if (frame_ts >= 0) {
++- AVStream *s = priv->format_ctx->streams[priv->stream_index];
++- frame_ts = av_rescale_q(frame_ts, s->time_base, AV_TIME_BASE_Q);
+++ int64_t ts;
+++ if (priv->frame->pts >= 0) {
+++ ts = priv->frame->pts;
+++ } else if (priv->frame->pkt_dts >= 0) {
+++ ts = priv->frame->pkt_dts;
++ } else {
++- frame_ts = priv->prev_frame_end;
+++ d_print("AVFrame.pts and AVFrame.pkt_dts are unset\n");
+++ return -1;
++ }
++
++- if (frame_ts >= priv->seek_ts)
++- return 1;
++-
++- int64_t frame_dur = av_rescale(priv->frame->nb_samples,
++- AV_TIME_BASE, priv->frame->sample_rate);
++- int64_t frame_end = frame_ts + frame_dur;
++- priv->prev_frame_end = frame_end;
+++ AVStream *s = priv->format_ctx->streams[priv->stream_index];
+++ double frame_ts = ts * av_q2d(s->time_base);
++
++- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n",
++- priv->seek_ts, frame_ts, frame_end);
+++ d_print("seek_ts: %.6fs, frame_ts: %.6fs\n", priv->seek_ts, frame_ts);
++
++- if (frame_end <= priv->seek_ts)
+++ if (frame_ts >= priv->seek_ts)
++ return 0;
+++ return (priv->seek_ts - frame_ts) * priv->frame->sample_rate;
+++}
++
++- int64_t skip_samples = av_rescale(priv->seek_ts - frame_ts,
++- priv->frame->sample_rate, AV_TIME_BASE);
++- priv->frame->nb_samples -= skip_samples;
+++static void ffmpeg_skip_frame_part(struct ffmpeg_private *priv)
+++{
+++ if (priv->skip_samples >= priv->frame->nb_samples) {
+++ d_print("skipping frame: %d samples\n",
+++ priv->frame->nb_samples);
+++ priv->skip_samples -= priv->frame->nb_samples;
+++ priv->frame->nb_samples = 0;
+++ return;
+++ }
++
++ int bps = av_get_bytes_per_sample(priv->frame->format);
++ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100)
++@@ -322,15 +321,17 @@ static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts)
++ int channels = priv->codec_ctx->channels;
++ #endif
++
+++ priv->frame->nb_samples -= priv->skip_samples;
+++
++ /* Just modify frame's data pointer because it's throw-away */
++ if (av_sample_fmt_is_planar(priv->frame->format)) {
++ for (int i = 0; i < channels; i++)
++- priv->frame->extended_data[i] += skip_samples * bps;
+++ priv->frame->extended_data[i] += priv->skip_samples * bps;
++ } else {
++- priv->frame->extended_data[0] += skip_samples * channels * bps;
+++ priv->frame->extended_data[0] += priv->skip_samples * channels * bps;
++ }
++- d_print("skipping %ld samples\n", skip_samples);
++- return 1;
+++ d_print("skipping %ld samples\n", priv->skip_samples);
+++ priv->skip_samples = 0;
++ }
++
++ /*
++@@ -361,17 +362,16 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv)
++ if (res < 0)
++ return res;
++
++- int64_t frame_ts = -1;
++- if (priv->frame->pts >= 0)
++- frame_ts = priv->frame->pts;
++- else if (priv->frame->pkt_dts >= 0)
++- frame_ts = priv->frame->pkt_dts;
+++ if (priv->seek_ts > 0) {
+++ priv->skip_samples = ffmpeg_calc_skip_samples(priv);
+++ if (priv->skip_samples >= 0)
+++ priv->seek_ts = -1;
+++ }
++
++- if (priv->seek_ts > 0 && (frame_ts >= 0 || priv->prev_frame_end >= 0)) {
++- if (ffmpeg_seek_into_frame(priv, frame_ts) == 0)
+++ if (priv->skip_samples > 0) {
+++ ffmpeg_skip_frame_part(priv);
+++ if (priv->frame->nb_samples == 0)
++ return 0;
++- priv->seek_ts = -1;
++- priv->prev_frame_end = -1;
++ }
++ return 1;
++ }
++@@ -434,8 +434,8 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
++ struct ffmpeg_private *priv = ip_data->private;
++ AVStream *st = priv->format_ctx->streams[priv->stream_index];
++
++- priv->seek_ts = offset * AV_TIME_BASE;
++- priv->prev_frame_end = -1;
+++ priv->seek_ts = offset;
+++ priv->skip_samples = 0;
++ int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num);
++
++ int ret = avformat_seek_file(priv->format_ctx,
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 17 Aug 2025 19:22:50 +0300
++Subject: ip/ffmpeg: don't process empty frames
++
++---
++ ip/ffmpeg.c | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index af6ecfb..dd9061a 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -356,7 +356,7 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv)
++ priv->curr_duration += priv->pkt->duration;
++
++ res = avcodec_send_packet(priv->codec_ctx, priv->pkt);
++- if (res == AVERROR(EAGAIN))
+++ if (res == 0 || res == AVERROR(EAGAIN))
++ return 0;
++ }
++ if (res < 0)
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Mon, 18 Aug 2025 03:32:22 +0300
++Subject: ip/ffmpeg: improve readability
++
++Previously ffmpeg_read()'s while loop was kinda leaking into
++ffmpeg_get_frame(), now it doesn't.
++---
++ ip/ffmpeg.c | 36 ++++++++++++++++++++----------------
++ 1 file changed, 20 insertions(+), 16 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index dd9061a..fc74895 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -337,30 +337,32 @@ static void ffmpeg_skip_frame_part(struct ffmpeg_private *priv)
++ /*
++ * return:
++ * <0 - error
++- * 0 - retry
+++ * 0 - eof
++ * >0 - ok
++ */
++ static int ffmpeg_get_frame(struct ffmpeg_private *priv)
++ {
++- int res = avcodec_receive_frame(priv->codec_ctx, priv->frame);
+++ int res;
+++retry:
+++ res = avcodec_receive_frame(priv->codec_ctx, priv->frame);
++ if (res == AVERROR(EAGAIN)) {
++ av_packet_unref(priv->pkt);
++ res = av_read_frame(priv->format_ctx, priv->pkt);
++ if (res < 0)
++- return res;
+++ goto err;
++
++ if (priv->pkt->stream_index != priv->stream_index)
++- return 0;
+++ goto retry;
++
++ priv->curr_size += priv->pkt->size;
++ priv->curr_duration += priv->pkt->duration;
++
++ res = avcodec_send_packet(priv->codec_ctx, priv->pkt);
++ if (res == 0 || res == AVERROR(EAGAIN))
++- return 0;
+++ goto retry;
++ }
++ if (res < 0)
++- return res;
+++ goto err;
++
++ if (priv->seek_ts > 0) {
++ priv->skip_samples = ffmpeg_calc_skip_samples(priv);
++@@ -371,9 +373,14 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv)
++ if (priv->skip_samples > 0) {
++ ffmpeg_skip_frame_part(priv);
++ if (priv->frame->nb_samples == 0)
++- return 0;
+++ goto retry;
++ }
++ return 1;
+++err:
+++ if (res == AVERROR_EOF)
+++ return 0;
+++ d_print("%s\n", ffmpeg_errmsg(res));
+++ return -IP_ERROR_INTERNAL;
++ }
++
++ static int ffmpeg_convert_frame(struct ffmpeg_private *priv)
++@@ -386,8 +393,10 @@ static int ffmpeg_convert_frame(struct ffmpeg_private *priv)
++ if (res >= 0) {
++ priv->swr_frame->nb_samples = res;
++ priv->swr_frame_start = 0;
+++ return res;
++ }
++- return res;
+++ d_print("%s\n", ffmpeg_errmsg(res));
+++ return -IP_ERROR_INTERNAL;
++ }
++
++ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count)
++@@ -401,16 +410,14 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun
++ while (count) {
++ if (priv->swr_frame->nb_samples == 0) {
++ res = ffmpeg_get_frame(priv);
++- if (res == AVERROR_EOF)
+++ if (res == 0)
++ break;
++- else if (res == 0)
++- continue;
++ else if (res < 0)
++- goto err;
+++ return res;
++
++ res = ffmpeg_convert_frame(priv);
++ if (res < 0)
++- goto err;
+++ return res;
++ }
++
++ int copy_frames = min_i(count, priv->swr_frame->nb_samples);
++@@ -424,9 +431,6 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun
++ written += copy_bytes;
++ }
++ return written;
++-err:
++- d_print("%s\n", ffmpeg_errmsg(res));
++- return -IP_ERROR_INTERNAL;
++ }
++
++ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Sun, 24 Aug 2025 19:16:57 +0300
++Subject: ip/ffmpeg: fix building for ffmpeg 8.0
++
++avcodec_close() can be safely removed because avcodec_free_context()
++is its replacement since 2016. See ffmpeg commit 2ef6dab0a79
++
++Builds with v3.3.9 v4.0.6 v6.1.3 v7.1.1 v8.0
++---
++ ip/ffmpeg.c | 1 -
++ 1 file changed, 1 deletion(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index fc74895..2cb0767 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -223,7 +223,6 @@ static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv,
++
++ static void ffmpeg_free(struct ffmpeg_private *priv)
++ {
++- avcodec_close(priv->codec_ctx);
++ avcodec_free_context(&priv->codec_ctx);
++ avformat_close_input(&priv->format_ctx);
++
--- /dev/null
--- /dev/null
++From: ihy123 <aladinandreyy@gmail.com>
++Date: Mon, 25 Aug 2025 11:17:06 +0300
++Subject: ip/ffmpeg: change sample format conversions
++
++---
++ ip/ffmpeg.c | 10 ++++------
++ 1 file changed, 4 insertions(+), 6 deletions(-)
++
++diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c
++index 2cb0767..2d3c610 100644
++--- a/ip/ffmpeg.c
+++++ b/ip/ffmpeg.c
++@@ -157,13 +157,11 @@ static void ffmpeg_set_sf_and_swr_opts(SwrContext *swr, AVCodecContext *cc,
++ av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0);
++ av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
++
++- *out_sample_fmt = cc->sample_fmt;
++- switch (*out_sample_fmt) {
++- case AV_SAMPLE_FMT_U8:
++- sf |= sf_bits(8) | sf_signed(0);
++- break;
++- case AV_SAMPLE_FMT_S32:
+++ switch (cc->sample_fmt) {
+++ case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP:
+++ case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P:
++ sf |= sf_bits(32) | sf_signed(1);
+++ *out_sample_fmt = AV_SAMPLE_FMT_S32;
++ break;
++ default:
++ sf |= sf_bits(16) | sf_signed(1);
--- /dev/null
--- /dev/null
++0001-atomic_ld.patch
++0002-fix-blhc.patch
++0003-ip-ffmpeg-more-precise-seeking.patch
++0004-ip-ffmpeg-skip-samples-only-when-needed.patch
++0005-ip-ffmpeg-remove-excessive-version-checks.patch
++0006-ip-ffmpeg-major-refactor.patch
++0007-Validate-sample-format-in-ip_open.patch
++0008-ip-ffmpeg-flush-swresample-buffer-when-seeking.patch
++0009-ip-ffmpeg-remember-swr_frame-s-capacity.patch
++0010-ip-ffmpeg-reset-swr_frame_start-when-seeking.patch
++0011-ip-ffmpeg-better-frame-skipping-logic.patch
++0012-ip-ffmpeg-don-t-process-empty-frames.patch
++0013-ip-ffmpeg-improve-readability.patch
++0014-ip-ffmpeg-fix-building-for-ffmpeg-8.0.patch
++0015-ip-ffmpeg-change-sample-format-conversions.patch
--- /dev/null
--- /dev/null
++#!/usr/bin/make -f
++# -*- makefile -*-
++
++include /usr/share/dpkg/architecture.mk
++ifneq ($(DEB_BUILD_ARCH),$(DEB_HOST_ARCH))
++CROSS = CROSS=$(DEB_HOST_GNU_TYPE)- PKG_CONFIG=$(DEB_HOST_GNU_TYPE)-pkg-config
++endif
++
++# The following architectures need the -latomic flag
++# to build, otherwise we FTBFS with
++# ./track_info.c:47: undefined reference to `__atomic_fetch_add_8'
++# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=935678
++# Depends on 13-atomic_ld.patch
++ifneq (,$(findstring $(DEB_HOST_ARCH), armel m68k mipsel powerpc sh4))
++export LDLIBS += -latomic
++endif
++
++DEB_CFLAGS_MAINT_APPEND += -I/usr/include/ncursesw
++DEB_BUILD_MAINT_OPTIONS = hardening=+all
++DPKG_EXPORT_BUILDFLAGS = 1
++include /usr/share/dpkg/buildflags.mk
++
++suggested_deps = pulse jack
++
++EXTRA_CMUS_DIR_OP_PLUGINS = debian/cmus/usr/lib/cmus/op/
++EXTRA_CMUS_PLUGINS := $(foreach plugin,$(suggested_deps),$(plugin).so)
++
++%:
++ dh $@ --with bash-completion
++
++override_dh_auto_configure:
++ $(CROSS) ./configure \
++ prefix=/usr \
++ CONFIG_ARTS=n \
++ CONFIG_ROAR=n \
++ DEBUG=0
++
++override_dh_auto_build:
++ # Pass V=2 to make to enable verbose build logs, which is useful for
++ # porters, sorting out build hardening issues, etc.
++ dh_auto_build -- V=2
++
++override_dh_install:
++ dh_install -pcmus
++ dh_movefiles -pcmus-plugin-ffmpeg --sourcedir=debian/cmus/ \
++ /usr/lib/cmus/ip/ffmpeg.so
++
++override_dh_installdocs:
++ dh_installdocs
++ # do not install zsh and bash completion twice
++ rm debian/cmus/usr/share/doc/cmus/contrib/_cmus \
++ debian/cmus/usr/share/doc/cmus/contrib/cmus.bash-completion
++
++override_dh_shlibdeps:
++ dh_shlibdeps -pcmus $(foreach plugin,$(EXTRA_CMUS_PLUGINS),-X$(plugin))
++ dpkg-shlibdeps -O -dSuggests \
++ $(foreach plugin,$(EXTRA_CMUS_PLUGINS),$(EXTRA_CMUS_DIR_OP_PLUGINS)$(plugin)) \
++ >> debian/cmus.substvars
++ dh_shlibdeps --remaining-packages
--- /dev/null
--- /dev/null
++---
++include:
++ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
--- /dev/null
--- /dev/null
++3.0 (quilt)
--- /dev/null
--- /dev/null
++Bug-Database: https://github.com/cmus/cmus/issues
++Bug-Submit: https://github.com/cmus/cmus/issues/new
++Repository: https://github.com/cmus/cmus.git
++Repository-Browse: https://github.com/cmus/cmus
--- /dev/null
--- /dev/null
++version=4
++opts="dversionmangle=s/\+git\d+$//,uversionmangle=s/([\d\.]+)-(rc|beta)(.*)/\1~\2\3/,filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/cmus-$1\.tar\.gz/" \
++https://github.com/cmus/cmus/tags .*/v?(\d\S*)\.tar\.gz